SpringBoot小例子进阶

Author Avatar
子语 2017 - 10 - 21
  • 在其它设备中阅读本文章

本部分代码是基于SpringBoot基础应用博文的。

代码结构如下:

demo   // 项目名
|- .idea
|- .mvn
|- src  // 代码存放区
   |- main 
      |- java
         |- com.example.demo          // java包
         	|- aspect                 // 拦截器
         	   |- HttpAspect.java     
         	|- controller             // 控制器
         	   |- PersonController   
         	|- domain                 // 实体类
         	   |- Person   
         	   |- Result              // 定义异常的响应信息的格式
         	|- enums
         	   |- ResultEnum          // 定义异常的code和信息
         	|- exception              // 自定义异常
         	   |- PersonException     
         	|- handle                 // 捕获异常
         	   |- ExceptionHandle 
         	|- repository             // jpa操作
         	   |- PersonRepository
         	|- service                // 处理业务逻辑
         	   |- PersonService  
            |- DemoApplication.java   // 项目启动类
      |- resources
         |- static     // 用于存放css,js等样式文件
         |- templates  // 用于存放html文件
         application.properties  // 项目配置文件
   |- test //测试代码存放区
|- target
.gitignore
mvnw
mvnw.cmd
demo.iml
pom.xml // 项目对象模型,添加项目依赖等配置

实现表单验证

要求: age小于18的数据无法入库

修改Person类

package com.example.demo.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.validation.constraints.Min;

@Entity
public class Person {
    @Id
    @GeneratedValue
    private Integer id;
    private String name;

    // @Min 使得该属性必须大于18,当数值小于18时,有提示信息
    @Min(value = 18, message = "年龄小于18无法添加")
    private Integer age;

    public Person() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

修改PersonController类addPerson()

     /**
     * 通过post方式访问localhost:8080/person?name=Jane&age=10可添加数据
     * @param person 要添加的person对象
     * @param bindingResult 获取绑定结果信息
     * @return 返回Person类信息
     */
    @PostMapping(value = "/person")
    public Person addPerson(@Valid Person person, BindingResult bindingResult) {
    	// 当输入数据不符合要求时,后台输出错误信息
        if (bindingResult.hasErrors()) {
            System.out.println(bindingResult.getFieldError().getDefaultMessage());
            return null;
        }
        return  personRepository.save(person);
    }

@Valid用于对传入的数据进行校验。

使用AOP处理请求

什么是AOP
(1)AOP(面向切面, Aspect Oriented Programming)是一种编程范式,是一种程序设计思想。思想是将通用逻辑从业务逻辑中分离出来
无法加载
将纵向的分析变为横向分析,从而将通用逻辑从业务逻辑中分离出来。
(2)除了AOP还有OOP(面向对象,Object Oriented Programming),POP(面向过程,Proceduer Oriented Programming)

AOP统一处理请求日志

要求:记录每一个http请求

添加依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

创建HttpAspect类

@Aspect实现切面注入;@Component将该类注册到SpringBoot容器中

package com.example.demo.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

// 切面注入
@Aspect
@Component
public class HttpAspect {
	// 日志对象
    private final static Logger LOGGER = LoggerFactory.getLogger(HttpAspect
            .class);

	// 指明哪些方法需要执行AOP
    @Pointcut("execution(public * com.example.demo.controller.PersonController" +
            ".*(..))")
    public void log() {}

    /**
     * 该方法功能是将请求中的信息输出至日志中
     * @Before 在使用PersonController中方法前先执行该注解下的方法
     * @param joinpoint 目标类连接点对象
     */
    @Before("log()")
    public void doBefore(JoinPoint joinpoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes)
                RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 获取url
        LOGGER.info("url = {}", request.getRequestURL());
        // 获取请求方式
        LOGGER.info("method = {}", request.getMethod());
        // 获取请求方的ip
        LOGGER.info("ip = {}", request.getRemoteAddr());
        // 获取被调用的方法名
        LOGGER.info("class_method = {}",
                joinpoint.getSignature().getDeclaringTypeName() +
                        "." + joinpoint.getSignature().getName() + "()");
        // 获取请求参数
        LOGGER.info("args = {}", joinpoint.getArgs());

    }

    /** 该方法功能是将响应中的信息输出至日志中
     * @AfterReturning 使得该类可以接收HttpResponse中对象信息
     * @param object 返回的对象信息
     */
    @AfterReturning(returning = "object", pointcut = "log()")
    public void doAfterReturning(Object object) {
        LOGGER.info("response = {}", object.toString());
    }
}

统一异常处理

要求:获取Person对象的age并判断
age < 12 : 返回"正在上小学"
age > 12 && age < 15: 返回"正在上初中"

定义异常响应信息格式

package com.example.demo.domain;


public class Result<T> {

    private Integer code; // 错误码
    private String msg;   // 提示信息
    private T data;       // 具体内容

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }
}

自定义异常类

package com.example.demo.exception;

import com.example.demo.enums.ResultEnum;

public class PersonException extends RuntimeException {
    private Integer code;

    public PersonException(ResultEnum resultEnum) {
        super(resultEnum.getMsg());
        this.code = resultEnum.getCode();
    }
    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

规范响应code和msg

package com.example.demo.enums;

public enum ResultEnum {
    UNKNOW_ERROR(-1, "未知错误"),
    SUCCESS(200, "添加成功"),
    PRIMARY_SCHOOL(403, "在上小学"),
    MIDDLE_SCHOOL(404, "在上初中"),
    ;
    private Integer code;
    private String msg;

    ResultEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}

进行异常捕获

package com.example.demo.handle;

import com.example.demo.domain.Result;
import com.example.demo.exception.PersonException;
import com.example.demo.util.ResultUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@ControllerAdvice
public class ExceptionHandle {
    private final static Logger LOGGER = LoggerFactory.getLogger
            (ExceptionHandle.class);
    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public Result handle(Exception e){
        if (e instanceof PersonException) {
            PersonException personException = (PersonException) e;
            return ResultUtil.error(personException.getCode(),
                    personException.getMessage());
        } else {
            LOGGER.error("[系统异常] {}", e);
            return ResultUtil.error(-1, "未知错误");
        }
    }
}

进行逻辑判断,从而判断异常

package com.example.demo.service;

import com.example.demo.domain.Person;
import com.example.demo.enums.ResultEnum;
import com.example.demo.exception.PersonException;
import com.example.demo.repository.PersonRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PersonService {
    @Autowired
    private PersonRepository personRepository;

    public void getAge(Integer id) throws Exception{
        Person person = personRepository.findOne(id);
        Integer age = person.getAge();
        if (age < 10) {
            throw new PersonException(ResultEnum.PRIMARY_SCHOOL);
        } else if (age > 10 && age < 16) {
            throw new PersonException(ResultEnum.MIDDLE_SCHOOL);
        }
    }
}

单元测试

对Service进行测试

package com.example.demo;

import com.example.demo.domain.Person;
import com.example.demo.service.PersonService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PersonServiceTest {
    @Autowired
    private PersonService personService;
    @Test
    public void findOneTest() {
        Person person = personService.findOne(9);
        // 断言
        Assert.assertEquals(new Integer(19), person.getAge());
    }
}

对API进行测试

package com.example.demo.controller;

import com.example.demo.domain.Person;
import com.example.demo.repository.PersonRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet
        .AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class PersonControllerTest {
    @Autowired
    private MockMvc mockMvc;
    @Test
    public void personList() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/person")).
                // 对状态码进行断言
                andExpect(MockMvcResultMatchers.status().isOk()).
                // 对响应文本进行断言
                andExpect(MockMvcResultMatchers.content().string(""));
    }

}

This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/框架/Spring Boot/SpringBoot小例子进阶/